﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krile.Kernel.Control;
using Krile.Kernel;
using System.IO;
using Twinkle.Dialogs;
using System.Drawing.Imaging;
using Krile.Kernel.WindowsEx;
using System.Text.RegularExpressions;

namespace Twinkle.Controls
{
    public partial class TInput : UserControl, IInputMethod
    {
        public Krile.Kernel.Bridges.IInputControlManager ParentICM { get; set; }

        HashSet<string> autoBindTags = new HashSet<string>();

        public TInput()
        {
            InitializeComponent();
            this.inputText.Font = Bridge.Config.DisplayConfig.RenderingFont;
            useAutomaticBinding.Checked = Kernel.Config.Behavior.UseTagAutoBinding;
            if (!String.IsNullOrEmpty(Kernel.Config.Behavior.PreviousStaticBindingHashtag))
            {
                bindingHashtagButton.Text = Kernel.Config.Behavior.PreviousStaticBindingHashtag;
            }
        }

        public MethodInvoker PostCallback { get; set; }
        public MethodInvoker CancelCallback { get; set; }

        public string IdentifyString
        {
            get { return Define.InputMethodId; }
        }

        public object ExtraData { get; set; }

        public string InputText
        {
            get
            {
                if (String.IsNullOrEmpty(bindingHashtagButton.Text))
                {
                    return inputText.Text;
                }
                else
                {
                    int rtidx = inputText.Text.IndexOf(" RT");
                    if (Kernel.Config.Behavior.AddHashtagBeforeRT && rtidx >= 0)
                    {
                        return inputText.Text.Substring(0, rtidx) + " " + bindingHashtagButton.Text + inputText.Text.Substring(rtidx);
                    }
                    else
                    {
                        return inputText.Text + " " + bindingHashtagButton.Text;
                    }
                }
            }
            set
            {
                inputText.Text = value;
            }
        }   

        public void Clear()
        {
            inputText.Clear();
            if (!String.IsNullOrEmpty(Kernel.Config.Behavior.PreviousStaticBindingHashtag))
            {
                bindingHashtagButton.Text = Kernel.Config.Behavior.PreviousStaticBindingHashtag;
            }
            else
            {
                bindingHashtagButton.Text = String.Empty;
            }
        }

        public void FocusToInput()
        {
            inputText.Focus();
        }

        public void Cancelled()
        {
            inputText.Clear();
        }

        public bool OnReplyRequired(ulong uid)
        {
            if (Bridge.StatusManager.IdExists(uid))
            {
                var s = Bridge.StatusManager.GetStatus(uid);
                var ts = s as Tweak.TStatus;
                if (ts != null && ts.StatusKind == Tweak.TStatus.StatusKinds.DirectMessage)
                {
                    this.inputText.SelectionStart = 0;
                    this.inputText.SelectedText = "d @" + s.User.Id + " ";
                    this.inputText.SelectionStart = this.inputText.Text.Length;
                    return true;
                }
                else
                {
                    if (this.inputText.Text.StartsWith("@"))
                    {
                        //replying sys
                        ParentICM.SetServices(new[] { Kernel.Plugin.TOperation.HostingService }, false);
                        this.inputText.SelectionStart = 0;
                        this.inputText.SelectedText = ".";
                        this.inputText.SelectionStart = this.inputText.Text.Length;
                        if (!this.inputText.Text.StartsWith(".@" + s.User.Id))
                        {
                            AppendUserId(s.User.Id);
                        }
                        return false;
                    }
                    else if (this.inputText.Text.StartsWith(".@"))
                    {
                        //starts with .@
                        ParentICM.SetServices(new[] { Kernel.Plugin.TOperation.HostingService }, false);
                        var replyings = from t in this.inputText.Text.Substring(1).Split(' ')
                                        where t.StartsWith("@")
                                        select t;
                        if (replyings.Contains<string>("@" + s.User.Id))
                            return false;
                        //append last
                        AppendUserId(s.User.Id);
                        return false;
                    }
                    else
                    {
                        this.inputText.SelectionStart = 0;
                        this.inputText.SelectedText = "@" + s.User.Id + " ";
                        this.inputText.SelectionStart = this.inputText.Text.Length;
                        return true;
                    }
                }
            }
            return true;
        }

        private void AppendUserId(string id)
        {
            //append last
            int count = 0;
            while (count + 1 < this.inputText.Text.Length && this.inputText.Text[count + 1] == '@')
            {
                count++;
                while (this.inputText.Text[count] != ' ')
                    count++;
            }

            this.inputText.SelectionStart = count + 1;
            this.inputText.SelectedText = "@" + id + " ";
            this.inputText.SelectionStart = this.inputText.Text.Length;
        }

        private void postButton_Click(object sender, EventArgs e)
        {
            if (Kernel.Config.External.URLShoteningService.Use)
                ConvertToCompressed();
            PostCallback.Invoke();
        }

        private void inputText_TextChanged(object sender, EventArgs e)
        {
            postButton.Enabled = !String.IsNullOrEmpty(inputText.Text) || !String.IsNullOrEmpty(bindingHashtagButton.Text);
            int htl = inputText.Text.Length;
            if (!String.IsNullOrEmpty(bindingHashtagButton.Text))
                htl = inputText.Text.Length + bindingHashtagButton.Text.Length + 1;
            if (htl > 140)
            {
                postButton.ForeColor = Color.Red;
                postButton.Font = new Font(postButton.Font, FontStyle.Bold);
            }
            else
            {
                postButton.ForeColor = SystemColors.ControlText;
                postButton.Font = new Font(postButton.Font, FontStyle.Regular);
            }
            if (Kernel.Config.Behavior.UseTagAutoBinding && Kernel.Config.Behavior.TagAutoBindingData != null)
            {
                Queue<string> applyQueue = new Queue<string>();
                Queue<string> removeQueue = new Queue<string>();
                foreach (var bind in Kernel.Config.Behavior.TagAutoBindingData)
                {
                    if (String.IsNullOrEmpty(bind.Keyword))
                        continue;
                    bool apply = false;
                    switch (bind.Type)
                    {
                        case Data.Config.BehaviorConfig.TagBinding.BindingType.Contains:
                            if (inputText.Text.IndexOf(bind.Keyword, StringComparison.CurrentCultureIgnoreCase) >= 0)
                                apply = true;
                            break;
                        case Data.Config.BehaviorConfig.TagBinding.BindingType.StartsWith:
                            if (inputText.Text.StartsWith(bind.Keyword, StringComparison.CurrentCultureIgnoreCase))
                                apply = true;
                            break;
                        case Data.Config.BehaviorConfig.TagBinding.BindingType.EndsWith:
                            if (inputText.Text.EndsWith(bind.Keyword, StringComparison.CurrentCultureIgnoreCase))
                                apply = true;
                            break;
                        case Data.Config.BehaviorConfig.TagBinding.BindingType.Equals:
                            if (inputText.Text.Equals(bind.Keyword, StringComparison.CurrentCultureIgnoreCase))
                                apply = true;
                            break;
                        case Data.Config.BehaviorConfig.TagBinding.BindingType.Regex:
                            if (Regex.IsMatch(inputText.Text, bind.Keyword, RegexOptions.IgnoreCase))
                                apply = true;
                            break;
                    }
                    if (apply)
                        applyQueue.Enqueue(bind.Tags);
                    else
                        removeQueue.Enqueue(bind.Tags);
                    DistinctTags();
                }
                while (removeQueue.Count > 0)
                    RemoveTags(removeQueue.Dequeue());
                while (applyQueue.Count > 0)
                    ApplyTags(applyQueue.Dequeue());
            }
            
            DistinctTags();

            if (String.IsNullOrEmpty(bindingHashtagButton.Text))
                postButton.Text = "Post [" + inputText.Text.Length.ToString() + "]";
            else
                postButton.Text = "Post [" + inputText.Text.Length.ToString() + "+" + (bindingHashtagButton.Text.Length + 1).ToString() + "]";
        }

        private void RemoveTags(string tags)
        {
            if (string.IsNullOrEmpty(tags))
                return;
            var split = from t in tags.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
                        let trimmed = t.TrimStart('#')
                        where TagContains(trimmed) && autoBindTags.Contains(t)
                        select t;

            List<string> bindingTags = new List<string>(bindingHashtagButton.Text.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries));

            foreach (var remove in split)
            {
                bindingTags.Remove(remove);
                autoBindTags.Remove(remove);
            }

            bindingHashtagButton.Text = String.Join(" ", bindingTags.ToArray());
        }

        private void ApplyTags(string tags)
        {
            if (string.IsNullOrEmpty(tags))
                return;
            var split = from t in tags.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
                        let trimmed = t.TrimStart('#')
                        where !TagContains(trimmed)
                        select t;

            if (split.Count() == 0) // no change.
                return;

            // uniquify
            split = split.Distinct();

            foreach (var tag in split)
                autoBindTags.Add(tag);

            if (String.IsNullOrEmpty(bindingHashtagButton.Text))
                bindingHashtagButton.Text += String.Join(" ", split.ToArray());
            else
                bindingHashtagButton.Text += " " + String.Join(" ", split.ToArray());
        }

        private void DistinctTags()
        {
            var match = Common.XStringGenerator.HashRegex.Matches(inputText.Text);
            if (match.Count > 0)
            {
                HashSet<string> textTags = new HashSet<string>();
                foreach (var m in match.Cast<Match>())
                {
                    if (m.Success)
                    {
                        textTags.Add(m.Captures[0].Value);
                    }
                }
                var tags = from t in bindingHashtagButton.Text.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
                           select t;
                var newtags = tags.Except(textTags);
                if (newtags.Count() > 0)
                {
                    bindingHashtagButton.Text = String.Join(" ", newtags.ToArray());
                }
                else
                {
                    bindingHashtagButton.Text = String.Empty;
                }
            }


            // Common.XStringGenerator.HashRegex

        }

        private bool TagContains(string tagCandidate)
        {
            var tags = from t in bindingHashtagButton.Text.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries)
                       select t.TrimStart('#');
            return tags.Contains(tagCandidate);
        }

        bool converted = false;
        private void inputText_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.A && e.Control)
            {
                e.Handled = true;
                e.SuppressKeyPress = true;
                inputText.SelectAll();
            }
            else if (e.KeyCode == Keys.Enter && e.Control)
            {
                e.Handled = true;
                e.SuppressKeyPress = true;
                PostCallback();
            }
            else if ((e.KeyCode == Keys.Space || e.KeyCode == Keys.Enter))
            {
                if (!converted)
                {
                    ConvertToCompressed();
                    converted = true;
                }
            }
            else if (e.KeyCode == Keys.V && e.Control)
            {
                e.Handled = true;
                e.SuppressKeyPress = true;
                inputText.SelectedText = Clipboard.GetText();
                ConvertToCompressed();
            }
            else
            {
                converted = false;
            }
        }

        private void ConvertToCompressed()
        {
            if (!Kernel.Config.External.URLShoteningService.Use) return;
            if (Kernel.Config.External.URLShoteningService.UseOnlyExceeds && inputText.Text.Length <= 140) return;
            try
            {
                this.Enabled = false;
                var rgx = Krile.Common.Regexs.URLRegex;
                int selectStart = inputText.SelectionStart;
                var rep = rgx.Replace(inputText.Text, (o) =>
                {
                    if (o.Captures.Count == 0 || URLShortener.URLShortenManager.IsCompressedThis(o.Value))
                        return o.Value;
                    var sug = URLShortener.URLShortenManager.GetSuggestedCompressor();
                    string nv;
                    if (sug.TryCompress(o.Value, out nv))
                        return nv;
                    else
                        return o.Value;
                });
                inputText.Text = rep;
                if (selectStart <= inputText.Text.Length)
                    inputText.SelectionStart = selectStart;
                else
                    inputText.SelectionStart = inputText.Text.Length;
            }
            finally
            {
                this.Enabled = true;
                this.FocusToInput();
            }
        }

        private void uploadImageMenuItem_Click(object sender, EventArgs e)
        {
            using (var ofd = new OpenFileDialog())
            {
                ofd.Filter = "Images|*.png;*.gif;*.bmp;*.jpg;*.gif";
                ofd.Title = "Open image file";
                if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    try
                    {
                        UploadImage(Image.FromFile(ofd.FileName));
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Image open error:" + ex.Message);
                    }
                }

            }
        }

        private void uploadDesktopMenuItem_Click(object sender, EventArgs e)
        {
            Rectangle rect = Screen.PrimaryScreen.Bounds;
            Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);

            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(rect.X, rect.Y, 0, 0, rect.Size, CopyPixelOperation.SourceCopy);
            }

            UploadImage(bmp);
        }

        private void uploadClipboardMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                var img = Clipboard.GetImage();
                if (img != null)
                    UploadImage(img);
                else
                    MessageBoxEx.ShowDialog(this, Lang.Msg.ClipboardError_Context, Lang.Msg.ClipboardError_NotImage, Lang.Msg.ClipboardError_Title, MessageBoxEx.MessageBoxExButtons.Close, MessageBoxEx.MessageBoxExIcon.Error);
            }
            catch (Exception ex)
            {
                MessageBoxEx.ShowDialog(this, Lang.Msg.ClipboardError_Context, ex.Message, Lang.Msg.ClipboardError_Title, MessageBoxEx.MessageBoxExButtons.Close, MessageBoxEx.MessageBoxExIcon.Error);
            }

        }

        private void UploadImage(Image img)
        {
            using (var iu = new ImageUploader(img, inputText.Text))
            {
                iu.ShowDialog();
                if (iu.UploadedUrl != null)
                {
                    inputText.Text = inputText.Text + " " + iu.UploadedUrl;
                }
            }
        }

        private void bindingHashtagsMenuItem_Click(object sender, EventArgs e)
        {
            using (var editTag = new Twinkle.Dialogs.Hashtag.HashtagSelector(bindingHashtagButton.Text))
            {
                if (editTag.ShowDialog() == DialogResult.OK)
                {
                    bindingHashtagButton.Text = editTag.Hashtags;
                    Kernel.Config.Behavior.PreviousStaticBindingHashtag = editTag.Hashtags;
                    autoBindTags.Clear();
                    inputText_TextChanged(this, EventArgs.Empty);
                }
            }
        }

        private void clearBinding_Click(object sender, EventArgs e)
        {
            bindingHashtagButton.Text = String.Empty;
            Kernel.Config.Behavior.PreviousStaticBindingHashtag = String.Empty;
            autoBindTags.Clear();
            inputText_TextChanged(sender, e);
        }

        private void useAutomaticBinding_Click(object sender, EventArgs e)
        {
            Kernel.Config.Behavior.UseTagAutoBinding = useAutomaticBinding.Checked;
        }

        private void bindingHashtagButton_TextChanged(object sender, EventArgs e)
        {
            clearBinding.Visible = !String.IsNullOrEmpty(bindingHashtagButton.Text);
            inputText.UpdateColor();
            inputText.TagLength = bindingHashtagButton.Text.Length;
        }

    }
}